home *** CD-ROM | disk | FTP | other *** search
- #!/usr/bin/perl
-
- #require 5.005;
-
- # Copyright Marc Lehmann <pcg@goof.com>
- #
- # This is part of the Gimp-Perl extension, and shares its copright with it.
-
- # this file is called "the dong"
-
- # TODO
- # more syntax ;) more functions ;) more exprns ;) more constants ;)
- # ui/args
- # too many parens
- # comments(!)
-
- # This is distributed under the GPL (see COPYING.GNU for details).
-
- =cut
-
- =head1 NAME
-
- scm2perl - convert script-fu to perl
-
- =head1 SYNOPSIS
-
- scm2perl filename.scm...
-
- =head1 DESCRIPTION
-
- This program tries to convert Script-Fu (Scheme) scripts written for The
- Gimp into a Perl script.
-
- Don't expect too much from this version. To run it, you need
- the Parse::RecDescent module from CPAN.
-
- =head1 CONVERSION TIPS
-
- =head2 PDB functions returning arrays
-
- Perl knows the length of arrays, Script-Fu doesn't. Functions returning
- single arrays return them as a normal perl array, Functions returning
- more then one array return it as an array-ref. Script-Fu (and the
- converted script) expect to get a length argument and then the
- arguments. Each occurrence (common ones are C<gimp_list_images> or
- C<gimp_image_get_layers>) must be fixed by hand.
-
- =head1 AUTHOR
-
- Marc Lehmann <pcg@goof.com>
-
- =head1 SEE ALSO
-
- gimp(1), L<Gimp>.
-
- =cut
-
- $|=1;
-
- use Parse::RecDescent;
-
- $RD_HINT=1;
- #$RD_TRACE=1;
-
- unless(@ARGV) {
- print STDERR "Script-Fu to Perl Translator 1.0\n";
- print STDERR "Usage: $0 file.scm ...\n";
- exit(1);
- }
-
- print STDERR "creating parser..." unless $quiet;
-
- $parser = new Parse::RecDescent <<'EOA';
-
- {
- # use re 'eval';
- $Parse::RecDescent::tokensep = '(?:\s*(?:(;[^\n]*\n))?)*';
-
- my $indent = 0;
- my %sf2pf = (
- 'SF-IMAGE' => 'PF_IMAGE, ',
- 'SF-LAYER' => 'PF_LAYER, ',
- 'SF-CHANNEL' => 'PF_CHANNEL, ',
- 'SF-VALUE' => 'PF_VALUE, ',
- 'SF-TOGGLE' => 'PF_TOGGLE, ',
- 'SF-DRAWABLE' => 'PF_DRAWABLE, ',
- 'SF-STRING' => 'PF_STRING, ',
- 'SF-COLOR' => 'PF_COLOUR, ',
- 'SF-ADJUSTMENT' => 'PF_ADJUSTMENT,',
- 'SF-FONT' => 'PF_FONT, ',
- 'SF-PATTERN' => 'PF_PATTERN, ',
- 'SF-GRADIENT' => 'PF_GRADIENT, ',
- 'SF-FILENAME' => 'PF_FILE, ',
- );
- my %constant = qw(
- TRUE 1
- FALSE 0
- #t 1
- #f 0
-
- RGB RGB_IMAGE
- RGBA RGBA_IMAGE
-
- LINEAR LINEAR_INTERPOLATION
-
- NORMAL NORMAL_MODE
- ADDITION ADDITION_MODE
- MULTIPLY MULTIPLY_MODE
- DIFFERENCE DIFFERENCE_MODE
- DARKEN_ONLY DARKEN_ONLY_MODE
- LIGHTEN_ONLY LIGHTEN_ONLY_MODE
- BEHIND BEHIND_MODE
- COLOR COLOR_MODE
- DISSOLVE DISSOLVE_MODE
- HUE HUE_MODE
- OVERLAY OVERLAY_MODE
- SATURATION SATURATION_MODE
- SCREEN SCREEN_MODE
- SUBTRACT SUBTRACT_MODE
- VALUE VALUE_MODE
-
- ALPHA_MASK ADD_ALPHA_MASK
- BLACK_MASK ADD_BLACK_MASK
- WHITE_MASK ADD_WHITE_MASK
-
- *pi* 3.14159265
- );
- my $constants = join("|",map {quotemeta} sort {length($b) <=> length($a)} keys %constant);
- my %compat_fun = (
- cdr => 'sub cdr {
- my(@x)=@{$_[0]};
- shift(@x);
- @x >1 ? [@x] : $x[0];
- }',
-
- cddr => 'sub cddr {
- my(@x)=@{$_[0]};
- shift(@x); shift(@x);
- @x >1 ? [@x] : $x[0];
- }',
-
- max => 'sub max {
- $_[0] > $_[1] ? $_[0] : $_[1];
- }',
-
- min => 'sub min {
- $_[0] < $_[1] ? $_[0] : $_[1];
- }',
-
- fmod => 'sub fmod {
- $_[0] - int($_[0]/$_[1])*$_[1];
- }',
-
- 'number->string' => 'sub number2string {
- sprintf "%$_[1]d",$_[0];
- }',
-
- nth => 'sub nth {
- $_[1]->[$_[0]];
- }',
-
- );
- my $xskip;
-
- my $compat_fun = join("|",map {quotemeta} sort {length($b) <=> length($a)} keys %compat_fun);
-
- sub func2perl {
- my($name)=@_;
- $name=~s/->/2/g;
- $name=~y/-*<>?!:\//_/;
- $name=~/^[A-Za-z_]/ ? $name : "_$name";
- }
-
- sub sf2pf {
- my $name=lc $_[0];
- $name=~y/ -?!:<>\[]/__/d;
- $name=~s/_*[()].*$/"/;
- $name=~s/_\d*_/_/g;
- $name=~s/_+$//;
- sprintf "%-20s","'$name',";
- }
- }
-
- script : ( ...!/$/ stmt)(s) nl /$/
- | <error:unable to recognize next statement>
-
- stmts : ( ...!')' nl stmt)(s?)
-
- stmt : '(' command ')'
- | expr gen[";"]
-
- command : cp_expr gen[";"]
- | c_let
- | c_set
- | c_if
- | c_while
- | e_cond gen[";"]
- | c_aset
- | c_defun
- | c_define
- | c_reg
- | /print\b/ gen["print "] expr gen[",'\n';"]
- | e_call gen[";"]
- | atom gen[";"]
- | <error:unrecognized statement>
-
- expr : '(' e_if ')'
- | '(' gen["("] e_cond gen[")"] ')'
- | '(' cp_expr ')'
- | '(' ...!pdbfun e_call ')'
- | '(' ...pdbfun gen["["] e_call gen["]"] ')'
- | '(' gen["do {"] incindent nl command decindent nl gen["}"] ')'
- | atom
- | ...!')' <error:unrecognized expression>
-
- cp_expr : /car\b/ '(' ...pdbfun e_call ')'
-
- | e_begin
- | e_list
- | '=' expr 'TRUE'
- | '=' 'TRUE' expr
- | '=' gen["!"] expr 'FALSE'
- | '=' gen["!"] 'FALSE' expr
- | '-' gen["-("] expr ...')' gen[")"]
- | m{[-+]|and\b} gen["("] e_binop[$item[1]] gen[")"]
- | m{<=|>=|!=|[*/<>]|or\b} e_binop[$item[1]]
- | '=' e_binop["=="]
- | /eq\?|eqv\?|equal\?/ '()' expr gen[" eq ''"] #X#
- | /eq\?|eqv\?|equal\?/ e_binop["eq"]
- | /realtime\b/ gen["time"]
- | /modulo\b/ expr gen[" % "] expr
- | 'divide?' gen["!"] expr gen["%"] expr
- | 'string-append' expr (...!')' gen["."] expr)(s?)
- | 'number->string' expr ...')'
- | 'cons-array' gen["("] expr (gen[","] expr)(?) gen[",[])"]
- | 'symbol-bound?' string '(' ident ')' gen["0"]
-
- | /aref\b/ expr gen["->["] expr gen["]"]
-
- | /$compat_fun/ { $::add_funcs{$compat_fun{$item[1]}}++ } <reject>
- | /car\b/ gen["\@{"] expr gen["}[0]"]
- | /cadr\b/ gen["\@{"] expr gen["}[1]"]
- | /caddr\b/ gen["\@{"] expr gen["}[2]"]
- | 'null?' gen["!\@{"] expr gen["}"]
- | /cons\b/ gen["["] expr gen[", "] expr gen["]"]
-
- | ...')' gen["[]"]
- | '(' cp_expr ')'
- | constant
-
- pdbfun : /gimp-|plug-in-|script-fu-|file-|extension-/
-
- atom : constant
- | 'gimp-data-dir' gen["'/usr/local/share/gimp'"]
- | ident gen["\$$item[-1]"]
- | numeral
- | string gen[$item[-1]]
- | list
- | "'not-guile" gen["1"]
-
- e_dot : 'string-append' expr gen["."] expr
-
- c_defun : 'define' '(' <commit> ident
- nl gen["sub $item[-2] {"] incindent
- nl (...!')'
- gen["my ("]
- pardef (...!')' gen[", "] pardef)(s?)
- gen[") = \@_;"]
- )(?)
- ')'
- stmts decindent
- nl gen["}"] nl
-
- #c_define: 'define' ident gen["sub $item[-1] {"] incindent
- # (nl command | stmts ) decindent
- # nl gen["}"] nl
-
- c_define: 'define' ident gen["\$$item[-1] = "] expr gen[";"]
-
- pardef : ident gen["\$$item[-1]"]
-
- c_reg : 'script-fu-register' <commit>
- string string string
- string string string
- string
- {
- $item[1]=func2perl(substr($item[3],1,length($item[3])-2));
- $item[3]=~s/script-fu/perl_fu/;
- $item[3]=~y/-/_/;
- $item[4]=~s/Script-Fu/Perl-Fu/;
- $item[5]=~s/\s{2,}/ /g;
- }
- nl gen["register "] incindent
- gen[$item[3]] gen[","]
- nl gen[$item[5]] gen[","]
- nl gen[$item[5]] gen[","]
- nl gen[$item[6]] gen[","]
- nl gen[$item[7]] gen[","]
- nl gen[$item[8]] gen[","]
- nl gen[$item[4]] gen[","]
- nl gen[$item[9]] gen[","]
- nl gen["["] incindent
- ( <reject:$arg[0]!~/^.<Image>/> skip paramdef paramdef unskip )[$item[4]](?)
- (...!')' paramdef)(s?)
- decindent
- nl gen["],"]
- nl gen["\\&$item[1];"]
- decindent
-
- paramdef: /SF-\w+/
- nl
- gen["["] gen[$sf2pf{$item[1]}]
- string gen[sf2pf($item[-1])."$item[-1], "]
- ( '"TRUE"' gen["1"]
- | '"FALSE"' gen["0"]
- | expr
- ) gen["],"]
-
- e_call : ( /script-fu-[A-Za-z_*][A-Za-z0-9-_*]*/
- gen["\"$item[-1]\"->(RUN_NONINTERACTIVE, "]
- | ident gen["$item[-1] ("]
- )
- (...!')'
- expr (...!')' gen[", "] expr)(s?)
- )[@arg](?)
- gen[")"]
-
- c_set : /set!?/ <commit>
- ident gen["\$$item[-1] = "]
- expr
- gen[";"]
-
- c_aset : /aset\b/ <commit>
- ident gen["\$$item[-1]\->["] expr gen["] = "] expr gen[";"]
-
- c_let : /let(\*|rec)?/ <commit>
- gen["do {"] incindent
- '(' let_expr(s) ')' nl
- stmts (expr gen[";"])(?) decindent
- nl gen["};"]
-
- let_expr: ...!')' nl '(' ident gen["my \$$item[-1] = "] expr gen[";"] ')'
-
- e_begin : /begin\b|prog1\b/ <commit>
- gen["do {"] incindent
- stmts decindent
- nl gen["}"]
-
- e_if : 'if' <commit>
- gen["("] expr gen[") ? ("] expr gen[") : ("] expr gen[")"]
-
- c_if : 'if' <commit>
- gen["if ("] expr gen[") {"] incindent
- nl stmt decindent
- nl gen["}"]
- ( '(' ')'
- |
- (...!')'
- gen[" else {"] incindent
- nl stmt decindent
- nl gen["}"]
- )(?)
- )
-
- c_while : 'while' <commit>
- nl gen["while ("] expr gen[") {"] incindent
- stmts decindent
- nl gen["}"]
-
- e_cond : 'cond' <commit>
- cond
-
- cond : '('
- ( /'?else\b/ expr ')'
- | expr gen[" ? "] expr incindent nl gen[": "] ')' decindent
- ( ...'(' cond | gen["die 'cond fell off the end'"] )
- )
-
- e_binop : expr
- (...!')'
- gen[" $arg[0] "]
- expr
- )[@arg](s?)
-
- e_list : 'list' gen["["] (expr (...!')' gen[", "] expr)(s?))(?) gen["]"]
-
- ident : /[A-Za-z0-9-#_*!?<>=\/]+/ <reject:$item[1]!~/[A-Za-z]/>
- { func2perl($item[1]) }
-
- numeral : /-?(?:\d+(?:\.\d*)?|\.\d+)/ gen[$item[-1]]
-
- string : /"([^\\"]+|\\.)*"/ { $item[1]=~s/([\$\@])/\\$1/g; $item[1] }
- | /'[A-Za-z0-9-_*!?<>=\/]+/ { $item[1]=~s/([\$\@])/\\$1/g; '"'.substr($item[1],1).'"' }
-
- list : "'(" gen["["] (expr (...!')' gen[", "] expr)(s?))(?) gen["]"] ')'
-
- constant: /(?:$constants)(?=[ \t;)\n\r])/ gen[$constant{$item[-1]}]
- | /[A-Z-_]{3,}/ gen[func2perl($item[-1])]
-
-
- nl: gen["\n".(" " x $indent)]
- incindent: { printf STDERR " %2d%%\b\b\b\b",$thisoffset*100/$::filesize unless $::quiet } { $indent++ }
- decindent: { $indent-- }
- skip: { $xskip++ }
- unskip: { $xskip-- }
- gen: ( <reject:$xskip> <defer: print ::OUT $arg[0] > )[@arg](?)
- #gen: { $xskip or print $arg[0] } #d#
-
- EOA
-
- $parser or die;
- print STDERR "done\n" unless $quiet;
-
- #$RD_TRACE=15;
-
- sub convert {
- my($in,$out)=@_;
-
- open IN,"<$in\0" or die "unable to open '$in' for reading: $!";
- open OUT,">$out\0" or die "unable to open '$out' for writing: $!";
-
- print STDERR "header..." unless $quiet;
- print OUT <<EOA;
- #!/usr/bin/perl
-
- use Gimp qw(:auto);
- use Gimp::Fu;
- EOA
-
- print STDERR "reading($in)..." unless $quiet;
- { local $/; $file = <IN> }
- $file =~ s/;.*?$//gm;
- $::filesize = length $file; # make it clear this is a _global_ variable
-
- print STDERR "translating..." unless $quiet;
- $parser->script ($file);
-
- print STDERR "trailer..." unless $quiet;
- print OUT "\n",join("\n\n",keys %add_funcs),"\n" if %add_funcs;
- print OUT <<'EOA';
-
- exit main;
- EOA
-
- print STDERR "wrote($out)\n" unless $quiet;
- }
-
- for $x (@ARGV) {
- (my $y=$x)=~s/\.scm/.pl/i or die "source file '$x' has no .scm extension";
- convert($x,$y);
- }
-
-